home *** CD-ROM | disk | FTP | other *** search
- /* source/moria2.c: misc code, mainly handles player movement, inventory, etc
-
- Copyright (c) 1989-92 James E. Wilson, Robert A. Koeneke
-
- This software may be copied and distributed for educational, research, and
- not for profit purposes provided that this copyright and statement are
- included in all such copies. */
-
- #include <stdio.h>
-
- #include "config.h"
- #include "constant.h"
- #include "types.h"
- #include "externs.h"
-
- #if defined(LINT_ARGS)
- static int see_wall(int, int, int);
- static int see_nothing(int, int, int);
- #else
- static int see_wall();
- #endif
-
-
- /* Change a trap from invisible to visible -RAK- */
- /* Note: Secret doors are handled here */
- void change_trap(y, x)
- register int y, x;
- {
- register cave_type *c_ptr;
- register inven_type *t_ptr;
-
- c_ptr = &cave[y][x];
- t_ptr = &t_list[c_ptr->tptr];
- if (t_ptr->tval == TV_INVIS_TRAP)
- {
- t_ptr->tval = TV_VIS_TRAP;
- lite_spot(y, x);
- }
- else if (t_ptr->tval == TV_SECRET_DOOR)
- {
- /* change secret door to closed door */
- t_ptr->index = OBJ_CLOSED_DOOR;
- t_ptr->tval = object_list[OBJ_CLOSED_DOOR].tval;
- t_ptr->tchar = object_list[OBJ_CLOSED_DOOR].tchar;
- lite_spot(y, x);
- }
- }
-
-
- /* Searches for hidden things. -RAK- */
- void search(y, x, chance)
- int y, x, chance;
- {
- register int i, j;
- register cave_type *c_ptr;
- register inven_type *t_ptr;
- register struct flags *p_ptr;
- bigvtype tmp_str, tmp_str2;
-
- p_ptr = &py.flags;
- if (p_ptr->confused > 0)
- chance = chance / 10;
- if ((p_ptr->blind > 0) || no_light())
- chance = chance / 10;
- if (p_ptr->image > 0)
- chance = chance / 10;
- for (i = (y - 1); i <= (y + 1); i++)
- for (j = (x - 1); j <= (x + 1); j++)
- if (randint(100) < chance) /* always in_bounds here */
- {
- c_ptr = &cave[i][j];
- /* Search for hidden objects */
- if (c_ptr->tptr != 0)
- {
- t_ptr = &t_list[c_ptr->tptr];
- /* Trap on floor? */
- if (t_ptr->tval == TV_INVIS_TRAP)
- {
- objdes(tmp_str2, t_ptr, TRUE);
- (void) sprintf(tmp_str,"You have found %s",tmp_str2);
- msg_print(tmp_str);
- change_trap(i, j);
- end_find();
- }
- /* Secret door? */
- else if (t_ptr->tval == TV_SECRET_DOOR)
- {
- msg_print("You have found a secret door.");
- change_trap(i, j);
- end_find();
- }
- /* Chest is trapped? */
- else if (t_ptr->tval == TV_CHEST)
- {
- /* mask out the treasure bits */
- if ((t_ptr->flags & CH_TRAPPED) > 1)
- if (!known2_p(t_ptr))
- {
- known2(t_ptr);
- msg_print("You have discovered a trap on the chest!");
- }
- else
- msg_print("The chest is trapped!");
- }
- }
- }
- }
-
-
- /* The running algorithm: -CJS-
-
- Overview: You keep moving until something interesting happens.
- If you are in an enclosed space, you follow corners. This is
- the usual corridor scheme. If you are in an open space, you go
- straight, but stop before entering enclosed space. This is
- analogous to reaching doorways. If you have enclosed space on
- one side only (that is, running along side a wall) stop if
- your wall opens out, or your open space closes in. Either case
- corresponds to a doorway.
-
- What happens depends on what you can really SEE. (i.e. if you
- have no light, then running along a dark corridor is JUST like
- running in a dark room.) The algorithm works equally well in
- corridors, rooms, mine tailings, earthquake rubble, etc, etc.
-
- These conditions are kept in static memory:
- find_openarea You are in the open on at least one
- side.
- find_breakleft You have a wall on the left, and will
- stop if it opens
- find_breakright You have a wall on the right, and will
- stop if it opens
-
- To initialize these conditions is the task of find_init. If
- moving from the square marked @ to the square marked . (in the
- two diagrams below), then two adjacent sqares on the left and
- the right (L and R) are considered. If either one is seen to
- be closed, then that side is considered to be closed. If both
- sides are closed, then it is an enclosed (corridor) run.
-
- LL L
- @. L.R
- RR @R
-
- Looking at more than just the immediate squares is
- significant. Consider the following case. A run along the
- corridor will stop just before entering the center point,
- because a choice is clearly established. Running in any of
- three available directions will be defined as a corridor run.
- Note that a minor hack is inserted to make the angled corridor
- entry (with one side blocked near and the other side blocked
- further away from the runner) work correctly. The runner moves
- diagonally, but then saves the previous direction as being
- straight into the gap. Otherwise, the tail end of the other
- entry would be perceived as an alternative on the next move.
-
- #.#
- ##.##
- .@...
- ##.##
- #.#
-
- Likewise, a run along a wall, and then into a doorway (two
- runs) will work correctly. A single run rightwards from @ will
- stop at 1. Another run right and down will enter the corridor
- and make the corner, stopping at the 2.
-
- #@ 1
- ########### ######
- 2 #
- #############
- #
-
- After any move, the function area_affect is called to
- determine the new surroundings, and the direction of
- subsequent moves. It takes a location (at which the runner has
- just arrived) and the previous direction (from which the
- runner is considered to have come). Moving one square in some
- direction places you adjacent to three or five new squares
- (for straight and diagonal moves) to which you were not
- previously adjacent.
-
- ...! ... EG Moving from 1 to 2.
- .12! .1.! . means previously adjacent
- ...! ..2! ! means newly adjacent
- !!!
-
- You STOP if you can't even make the move in the chosen
- direction. You STOP if any of the new squares are interesting
- in any way: usually containing monsters or treasure. You STOP
- if any of the newly adjacent squares seem to be open, and you
- are also looking for a break on that side. (i.e. find_openarea
- AND find_break) You STOP if any of the newly adjacent squares
- do NOT seem to be open and you are in an open area, and that
- side was previously entirely open.
-
- Corners: If you are not in the open (i.e. you are in a
- corridor) and there is only one way to go in the new squares,
- then turn in that direction. If there are more than two new
- ways to go, STOP. If there are two ways to go, and those ways
- are separated by a square which does not seem to be open, then
- STOP.
-
- Otherwise, we have a potential corner. There are two new open
- squares, which are also adjacent. One of the new squares is
- diagonally located, the other is straight on (as in the
- diagram). We consider two more squares further out (marked
- below as ?).
- .X
- @.?
- #?
- If they are both seen to be closed, then it is seen that no
- benefit is gained from moving straight. It is a known corner.
- To cut the corner, go diagonally, otherwise go straight, but
- pretend you stepped diagonally into that next location for a
- full view next time. Conversely, if one of the ? squares is
- not seen to be closed, then there is a potential choice. We check
- to see whether it is a potential corner or an intersection/room entrance.
- If the square two spaces straight ahead, and the space marked with 'X'
- are both blank, then it is a potential corner and enter if find_examine
- is set, otherwise must stop because it is not a corner. */
-
- /* The cycle lists the directions in anticlockwise order, for -CJS-
- over two complete cycles. The chome array maps a direction on
- to its position in the cycle.
- */
- static int cycle[] = { 1, 2, 3, 6, 9, 8, 7, 4, 1, 2, 3, 6, 9, 8, 7, 4, 1 };
- static int chome[] = { -1, 8, 9, 10, 7, -1, 11, 6, 5, 4 };
- static int find_openarea, find_breakright, find_breakleft, find_prevdir;
- static int find_direction; /* Keep a record of which way we are going. */
-
- void find_init(dir)
- int dir;
- {
- int row, col, deepleft, deepright;
- register int i, shortleft, shortright;
-
- row = char_row;
- col = char_col;
- if (!mmove(dir, &row, &col))
- find_flag = FALSE;
- else
- {
- find_direction = dir;
- find_flag = 1;
- find_breakright = find_breakleft = FALSE;
- find_prevdir = dir;
- if (py.flags.blind < 1)
- {
- i = chome[dir];
- deepleft = deepright = FALSE;
- shortright = shortleft = FALSE;
- if (see_wall(cycle[i+1], char_row, char_col))
- {
- find_breakleft = TRUE;
- shortleft = TRUE;
- }
- else if (see_wall(cycle[i+1], row, col))
- {
- find_breakleft = TRUE;
- deepleft = TRUE;
- }
- if (see_wall(cycle[i-1], char_row, char_col))
- {
- find_breakright = TRUE;
- shortright = TRUE;
- }
- else if (see_wall(cycle[i-1], row, col))
- {
- find_breakright = TRUE;
- deepright = TRUE;
- }
- if (find_breakleft && find_breakright)
- {
- find_openarea = FALSE;
- if (dir & 1)
- { /* a hack to allow angled corridor entry */
- if (deepleft && !deepright)
- find_prevdir = cycle[i-1];
- else if (deepright && !deepleft)
- find_prevdir = cycle[i+1];
- }
- /* else if there is a wall two spaces ahead and seem to be in a
- corridor, then force a turn into the side corridor, must
- be moving straight into a corridor here */
- else if (see_wall(cycle[i], row, col))
- {
- if (shortleft && !shortright)
- find_prevdir = cycle[i-2];
- else if (shortright && !shortleft)
- find_prevdir = cycle[i+2];
- }
- }
- else
- find_openarea = TRUE;
- }
- }
-
- /* We must erase the player symbol '@' here, because sub3_move_light()
- does not erase the previous location of the player when in find mode
- and when find_prself is FALSE. The player symbol is not draw at all
- in this case while moving, so the only problem is on the first turn
- of find mode, when the initial position of the character must be erased.
- Hence we must do the erasure here. */
- if (! light_flag && ! find_prself)
- print(loc_symbol(char_row, char_col), char_row, char_col);
-
- move_char(dir, TRUE);
- if (find_flag == FALSE)
- command_count = 0;
- }
-
- void find_run()
- {
- /* prevent infinite loops in find mode, will stop after moving 100 times */
- if (find_flag++ > 100)
- {
- msg_print("You stop running to catch your breath.");
- end_find();
- }
- else
- move_char(find_direction, TRUE);
- }
-
- /* Switch off the run flag - and get the light correct. -CJS- */
- void end_find()
- {
- if (find_flag)
- {
- find_flag = FALSE;
- move_light(char_row, char_col, char_row, char_col);
- }
- }
-
- /* Do we see a wall? Used in running. -CJS- */
- static int see_wall(dir, y, x)
- int dir, y, x;
- {
- char c;
-
- if (!mmove(dir, &y, &x)) /* check to see if movement there possible */
- return TRUE;
- #ifdef MSDOS
- else if ((c = loc_symbol(y, x)) == wallsym || c == '%')
- #else
- #ifdef ATARI_ST
- else if ((c = loc_symbol(y, x)) == (unsigned char)240 || c == '%')
- #else
- else if ((c = loc_symbol(y, x)) == '#' || c == '%')
- #endif
- #endif
- return TRUE;
- else
- return FALSE;
- }
-
- /* Do we see anything? Used in running. -CJS- */
- static int see_nothing(dir, y, x)
- int dir, y, x;
- {
- if (!mmove(dir, &y, &x)) /* check to see if movement there possible */
- return FALSE;
- else if (loc_symbol(y, x) == ' ')
- return TRUE;
- else
- return FALSE;
- }
-
-
- /* Determine the next direction for a run, or if we should stop. -CJS- */
- void area_affect(dir, y, x)
- int dir, y, x;
- {
- int newdir, t, inv, check_dir, row, col;
- register int i, max, option, option2;
- register cave_type *c_ptr;
-
- if (py.flags.blind < 1)
- {
- option = 0;
- option2 = 0;
- dir = find_prevdir;
- max = (dir & 1) + 1;
- /* Look at every newly adjacent square. */
- for(i = -max; i <= max; i++)
- {
- newdir = cycle[chome[dir]+i];
- row = y;
- col = x;
- if (mmove(newdir, &row, &col))
- {
- /* Objects player can see (Including doors?) cause a stop. */
- c_ptr = &cave[row][col];
- if (player_light || c_ptr->tl || c_ptr->pl || c_ptr->fm)
- {
- if (c_ptr->tptr != 0)
- {
- t = t_list[c_ptr->tptr].tval;
- if (t != TV_INVIS_TRAP && t != TV_SECRET_DOOR
- && (t != TV_OPEN_DOOR || !find_ignore_doors))
- {
- end_find();
- return;
- }
- }
- /* Also Creatures */
- /* the monster should be visible since update_mon() checks
- for the special case of being in find mode */
- if (c_ptr->cptr > 1 && m_list[c_ptr->cptr].ml)
- {
- end_find();
- return;
- }
- inv = FALSE;
- }
- else
- inv = TRUE; /* Square unseen. Treat as open. */
-
- if (c_ptr->fval <= MAX_OPEN_SPACE || inv)
- {
- if (find_openarea)
- {
- /* Have we found a break? */
- if (i < 0)
- {
- if (find_breakright)
- {
- end_find();
- return;
- }
- }
- else if (i > 0)
- {
- if (find_breakleft)
- {
- end_find();
- return;
- }
- }
- }
- else if (option == 0)
- option = newdir; /* The first new direction. */
- else if (option2 != 0)
- {
- end_find(); /* Three new directions. STOP. */
- return;
- }
- else if (option != cycle[chome[dir]+i-1])
- {
- end_find(); /* If not adjacent to prev, STOP */
- return;
- }
- else
- {
- /* Two adjacent choices. Make option2 the diagonal,
- and remember the other diagonal adjacent to the first
- option. */
- if ((newdir & 1) == 1)
- {
- check_dir = cycle[chome[dir]+i-2];
- option2 = newdir;
- }
- else
- {
- check_dir = cycle[chome[dir]+i+1];
- option2 = option;
- option = newdir;
- }
- }
- }
- else if (find_openarea)
- {
- /* We see an obstacle. In open area, STOP if on a side
- previously open. */
- if (i < 0)
- {
- if (find_breakleft)
- {
- end_find();
- return;
- }
- find_breakright = TRUE;
- }
- else if (i > 0)
- {
- if (find_breakright)
- {
- end_find();
- return;
- }
- find_breakleft = TRUE;
- }
- }
- }
- }
-
- if (find_openarea == FALSE)
- { /* choose a direction. */
- if (option2 == 0 || (find_examine && !find_cut))
- {
- /* There is only one option, or if two, then we always examine
- potential corners and never cur known corners, so you step
- into the straight option. */
- if (option != 0)
- find_direction = option;
- if (option2 == 0)
- find_prevdir = option;
- else
- find_prevdir = option2;
- }
- else
- {
- /* Two options! */
- row = y;
- col = x;
- (void) mmove(option, &row, &col);
- if (!see_wall(option, row, col)
- || !see_wall(check_dir, row, col))
- {
- /* Don't see that it is closed off. This could be a
- potential corner or an intersection. */
- if (find_examine && see_nothing(option, row, col)
- && see_nothing(option2, row, col))
- /* Can not see anything ahead and in the direction we are
- turning, assume that it is a potential corner. */
- {
- find_direction = option;
- find_prevdir = option2;
- }
- else
- /* STOP: we are next to an intersection or a room */
- end_find();
- }
- else if (find_cut)
- {
- /* This corner is seen to be enclosed; we cut the corner. */
- find_direction = option2;
- find_prevdir = option2;
- }
- else
- {
- /* This corner is seen to be enclosed, and we deliberately
- go the long way. */
- find_direction = option;
- find_prevdir = option2;
- }
- }
- }
- }
- }
-
-
- /* AC gets worse -RAK- */
- /* Note: This routine affects magical AC bonuses so that stores */
- /* can detect the damage. */
- int minus_ac(typ_dam)
- int32u typ_dam;
- {
- register int i, j;
- int tmp[6], minus;
- register inven_type *i_ptr;
- bigvtype out_val, tmp_str;
-
- i = 0;
- if (inventory[INVEN_BODY].tval != TV_NOTHING)
- {
- tmp[i] = INVEN_BODY;
- i++;
- }
- if (inventory[INVEN_ARM].tval != TV_NOTHING)
- {
- tmp[i] = INVEN_ARM;
- i++;
- }
- if (inventory[INVEN_OUTER].tval != TV_NOTHING)
- {
- tmp[i] = INVEN_OUTER;
- i++;
- }
- if (inventory[INVEN_HANDS].tval != TV_NOTHING)
- {
- tmp[i] = INVEN_HANDS;
- i++;
- }
- if (inventory[INVEN_HEAD].tval != TV_NOTHING)
- {
- tmp[i] = INVEN_HEAD;
- i++;
- }
- /* also affect boots */
- if (inventory[INVEN_FEET].tval != TV_NOTHING)
- {
- tmp[i] = INVEN_FEET;
- i++;
- }
- minus = FALSE;
- if (i > 0)
- {
- j = tmp[randint(i) - 1];
- i_ptr = &inventory[j];
- if (i_ptr->flags & typ_dam)
- {
- objdes(tmp_str, &inventory[j], FALSE);
- (void) sprintf(out_val, "Your %s resists damage!", tmp_str);
- msg_print(out_val);
- minus = TRUE;
- }
- else if ((i_ptr->ac+i_ptr->toac) > 0)
- {
- objdes(tmp_str, &inventory[j], FALSE);
- (void) sprintf(out_val, "Your %s is damaged!", tmp_str);
- msg_print(out_val);
- i_ptr->toac--;
- calc_bonuses();
- minus = TRUE;
- }
- }
- return(minus);
- }
-
-
- /* Corrode the unsuspecting person's armor -RAK- */
- void corrode_gas(kb_str)
- char *kb_str;
- {
- #ifdef ATARIST_MWC
- int32u holder;
- #endif
-
- #ifdef ATARIST_MWC
- if (!minus_ac((int32u) (holder = TR_RES_ACID)))
- #else
- if (!minus_ac((int32u) TR_RES_ACID))
- #endif
- take_hit(randint(8), kb_str);
- if (inven_damage(set_corrodes, 5) > 0)
- msg_print("There is an acrid smell coming from your pack.");
- }
-
-
- /* Poison gas the idiot. -RAK- */
- void poison_gas(dam, kb_str)
- int dam;
- char *kb_str;
- {
- take_hit(dam, kb_str);
- py.flags.poisoned += 12 + randint(dam);
- }
-
-
- /* Burn the fool up. -RAK- */
- void fire_dam(dam, kb_str)
- int dam;
- char *kb_str;
- {
- if (py.flags.fire_resist)
- dam = dam / 3;
- if (py.flags.resist_heat > 0)
- dam = dam / 3;
- take_hit(dam, kb_str);
- if (inven_damage(set_flammable, 3) > 0)
- msg_print("There is smoke coming from your pack!");
- }
-
-
- /* Freeze him to death. -RAK- */
- void cold_dam(dam, kb_str)
- int dam;
- char *kb_str;
- {
- if (py.flags.cold_resist)
- dam = dam / 3;
- if (py.flags.resist_cold > 0)
- dam = dam / 3;
- take_hit(dam, kb_str);
- if (inven_damage(set_frost_destroy, 5) > 0)
- msg_print("Something shatters inside your pack!");
- }
-
-
- /* Lightning bolt the sucker away. -RAK- */
- void light_dam(dam, kb_str)
- int dam;
- char *kb_str;
- {
- if (py.flags.lght_resist)
- take_hit((dam / 3), kb_str);
- else
- take_hit(dam, kb_str);
- if (inven_damage(set_lightning_destroy, 3) > 0)
- msg_print("There are sparks coming from your pack!");
- }
-
-
- /* Throw acid on the hapless victim -RAK- */
- void acid_dam(dam, kb_str)
- int dam;
- char *kb_str;
- {
- register int flag;
- #ifdef ATARIST_MWC
- int32u holder;
- #endif
-
- flag = 0;
- #ifdef ATARIST_MWC
- if (minus_ac((int32u) (holder = TR_RES_ACID)))
- #else
- if (minus_ac((int32u) TR_RES_ACID))
- #endif
- flag = 1;
- if (py.flags.acid_resist)
- flag += 2;
- take_hit (dam / (flag + 1), kb_str);
- if (inven_damage(set_acid_affect, 3) > 0)
- msg_print("There is an acrid smell coming from your pack!");
- }
-